//===============================================================================
// Microsoft patterns & practices Enterprise Library Contribution
// Resource Application Block
//===============================================================================

using System;
using System.Resources;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Microsoft.Practices.EnterpriseLibrary.Configuration.Design;
using EntLibContrib.Resource.Configuration.Design.Generators;

namespace EntLibContrib.Resource.Configuration.Design
{
	/// <summary>
	/// Generate strongly typed resources node command
	/// </summary>
	/// <remarks>This command will generate a source code class converting the resources
	/// from the configured resource provider into a strongly named type</remarks>
	public class GenerateStronglyTypedResourcesNodeCommand : ConfigurationNodeCommand
	{
		#region Construction
		/// <summary>
		/// Initializes a new instance of the <see cref="GenerateStronglyTypedResourcesNodeCommand"/> class.
		/// </summary>
		/// <param name="serviceProvider">The service provider.</param>
		public GenerateStronglyTypedResourcesNodeCommand(IServiceProvider serviceProvider)
			: base(serviceProvider)
		{ }
		#endregion

		#region Public Methods
		/// <summary>
		/// When overridden by a class, executes the core logic of the command.
		/// </summary>
		/// <param name="node">The node to execute the command upon.</param>
		protected override void ExecuteCore(ConfigurationNode node)
		{
			ResourceProviderNode sourceNode = node as ResourceProviderNode;
			if (sourceNode != null)
			{
				// check for dirty and ask for saving
				if (UIService.IsDirty(node.Hierarchy))
				{
					DialogResult result = UIService.ShowMessage(
						InternalResources.SaveApplicationBeforeGeneratingStronglyTypedResourcesRequest,
						InternalResources.SaveApplicationCaption,
						MessageBoxButtons.YesNo);
					if (DialogResult.Yes == result)
					{
						if (!TryAndSaveApplication(node))
							return;
					}
				}

				TryAndGenerateStronglyTypedResources(sourceNode);
			}
		}
		#endregion

		#region Private Methods
		private bool TryAndSaveApplication(ConfigurationNode node)
		{
			SaveConfigurationApplicationNodeCommand cmd = new SaveConfigurationApplicationNodeCommand(ServiceProvider);
			cmd.Execute(node);
			return cmd.SaveSucceeded;
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions")]
		private static void TryAndGenerateStronglyTypedResources(ResourceProviderNode sourceNode)
		{
			string resourceBaseName = sourceNode.ResourceProviderData.ResourceBaseName;
			if (!String.IsNullOrEmpty(resourceBaseName))
			{
				try
				{
					// get the configuration file name
					string configurationFileName = GetConfigurationFileName(sourceNode);

					// get the instance name
					string resourceInstance = sourceNode.Parent.Name;

					// get the class name
					string className = CreateClassName(resourceBaseName);

					// get the class filename
					SaveFileDialog dialog = new SaveFileDialog();
					dialog.Title = InternalResources.ResourceClassDialogTitle;
					dialog.Filter = InternalResources.ResourceClassDialogFilter;
					dialog.FileName = className;
					dialog.AddExtension = true;
					dialog.CheckPathExists = true;
					dialog.OverwritePrompt = true;
					if (dialog.ShowDialog() == DialogResult.OK)
					{
						// get the class file name
						string classFileName = dialog.FileName;
						if (String.IsNullOrEmpty(classFileName))
							classFileName = className + InternalResources.CSExtension;

						// get the namespace
						NamespaceDialog namespaceDialog = new NamespaceDialog();
						namespaceDialog.Namespace = GetNamespaceName(classFileName);
						if (namespaceDialog.ShowDialog() == DialogResult.OK)
						{
							string namespaceName = namespaceDialog.Namespace;
							if (String.IsNullOrEmpty(namespaceName))
								namespaceName = InternalResources.DefaultResourceNamespace;

							StronglyTypedResourceBuilderHelper.GenerateClass(configurationFileName, resourceInstance, namespaceName, className, classFileName);
							MessageBox.Show(String.Format(CultureInfo.CurrentCulture, InternalResources.StronglyTypedResourceClassMessage, namespaceName, className, classFileName), InternalResources.BlockName, MessageBoxButtons.OK, MessageBoxIcon.Information);
						}
					}
				}
				catch (MissingManifestResourceException exception)
				{
					MessageBox.Show(String.Format(CultureInfo.CurrentCulture, InternalResources.ExceptionNoResources, exception.Source), InternalResources.BlockName, MessageBoxButtons.OK, MessageBoxIcon.Error);
				}
				catch (ApplicationException exception)
				{
					MessageBox.Show(exception.Message, InternalResources.BlockName, MessageBoxButtons.OK, MessageBoxIcon.Error);
				}
			}
			else
				MessageBox.Show(InternalResources.ExceptionResourceBaseNameNullOrEmpty, InternalResources.BlockName, MessageBoxButtons.OK, MessageBoxIcon.Error);
		}
		
		private static string CreateClassName(string resourceBaseName)
		{
			string[] names = resourceBaseName.Trim().Split(' ');
			StringBuilder className = new StringBuilder(names.Length);
			foreach(string name in names)
			{
				string firstChar = name.Substring(0, 1).ToUpperInvariant();
				string otherChars = name.Substring(1, name.Length-1);
				className.Append(firstChar);
				className.Append(otherChars);
			}
			return className.ToString();
		}

		private static string GetConfigurationFileName(ConfigurationNode node)
		{
			ConfigurationNode currentNode = node;
			ConfigurationApplicationNode applicationNode;

			// navigate up the node tree until you reach the root node
			while (currentNode.Parent != null)
			{
				currentNode = currentNode.Parent;
				applicationNode = currentNode as ConfigurationApplicationNode;
				if (applicationNode != null)
					return applicationNode.ConfigurationFile;
			}

			return String.Empty;
		}

		[SuppressMessage("Microsoft.Performance", "CA1807:AvoidUnnecessaryStringCreation")]
		private static string GetNamespaceName(string fileName)
		{
			string namespaceName = String.Empty;

			if (fileName.Length > 0)
			{
				FileInfo fileInfo = new FileInfo(fileName);
				if (fileInfo.Exists)
				{
					string regex;
					switch (Path.GetExtension(fileName).ToLower(CultureInfo.CurrentCulture))
					{
						case ".cs":
							regex = InternalResources.CSRegularExpression;
							break;
						case ".vb":
							regex = InternalResources.VBRegularExpression;
							break;
						default :
							throw new ArgumentOutOfRangeException("fileName", InternalResources.ExceptionInvalidExtension);
					}
					using(StreamReader reader = fileInfo.OpenText())
					{
						Match match = Regex.Match(reader.ReadToEnd(), regex);
						if (match.Groups[1].Success)
							namespaceName = match.Groups[1].Value.Trim();
					}
				}
			}
			return namespaceName;
		}
		#endregion
	}
}
